/*********************************************************
*
*  FJH-sim v1.0
*  Copyright � 2008 H.Y. Bruintjes, J.H. Donker,
*  C.E. Etheredge
*
*  Simulator interface
*
**********************************************************/

#pragma once
#include <vector>
#include <string>
#include <fstream>

#include "node/Listener.h"
#include "node/Node.h"

class SimNode;
class SimulatorProtocolStack;

namespace simulator
{
	class Event;
	class Simulator;
	class Driver;

	const unsigned int DEFAULT_EYES = 1000;
	const unsigned int DEFAULT_TURNS = 1000;
	
	/*
		Rolls a dice a with a number of eyes.
		Without argument it will default to DEFAULT_EYES
	*/
	unsigned int dice ( unsigned int numberOfEyes=DEFAULT_EYES );

	struct Position
	{
				Position ( void )							{ x =0; y = 0; z = 0; }
				Position ( float x_, float  y_, float z_ )	{ x=x_; y = y_; z = z_; }

		float	x;
		float	y;
		float	z;
	};

	class EventNode: public Listener
	{
	public:
						EventNode		( Simulator* sim, bool eyeNode = false );
						EventNode		( float x, float y, float z, Simulator* sim, bool eyeNode = false );
						~EventNode		( void );
		
		Position		getPosition		( void );
		void			setPosition		( Position p );
		
		/*
			Buffer the events for this time period. Mangles them if multiple
			events arrive during the same time period. 
		*/		
		bool			eventArrive		( Event * event );
		Simulator *		getSimulator	( void );
		Node *			getNode			( void );
		
		void 			dataArrived		(const char * data, int len);

		/*
			Send a tick/pulse to the underlying protocol stack
		*/
		void			tick			( void );

		/*
			Custom userdata field
		*/
		void *			_userdata;
		unsigned long	_id;
		
		static void resetID() { NextSimID=0;};

	private:
		Position				position;
		std::vector<Event *>	receivedEvents;
		
		Node *					_pNode;
		Simulator*				sim;
		static unsigned long	NextSimID;
		
	protected:
		std::vector<std::string> interleave();
	};
	
	class Event
	{
	public:
						Event			( EventNode& node, std::string packet, unsigned long beginTime );
						~Event			( void );
		/*
			Mangles a the underlying message.
		*/		
		void			mangle			( void );
		/*
			Gives the time when this event was sent. (Time taken from the
			the internal clock.
		*/

		unsigned long	getBeginTime	( void );
		/*
			Gives a copy of the node that was used to send the event 
		*/
		Position		getPosition		( void );
		std::string		getPacket		( void );
		
		void			setRadius		( float _radius )	{ radius = _radius; }
		float			getRadius		( void )			{ return radius; }

		/*
			Custom userdata field
		*/
		void *			_userdata;
		unsigned long	_id;
		
		/*
			Source identifier
		*/
		unsigned int _sourceID;
	private:
		// Node is a copy of the orginating node, so stays safe when orginal
		// node moves
		Position		position;
		std::string		packet;
		unsigned long	beginTime;
		float			radius;

	};

	class Simulator
	{
	public:
								Simulator			( float speed, float range, int loseRate, int mangleRate, Driver* driver );
								~Simulator			( void );

		/*
			The main simulation loop. 
		*/
		void					run					( unsigned int turns = DEFAULT_TURNS );
		
		/*
			Handles all the events in the system.
			Mangles, loses, delivers events during a turn. 
		*/
		void					handleEvents		( void );
		
		/*
			Prints all stats of the simulation
		*/
		void					printStat			( void );
	
		float					getSpeed			( void );
		float					getRange			( void );
		//unsigned int			getErrorRate		( void );
		
		
		unsigned int			getLoseRate			( void );
		unsigned int			getMangleRate		( void );
		
		void					setSpeed			( float speed );
		void					setRange			( float range );
		//void					setErrorRate		( unsigned int errorRate );
		void					setLoseRate			( unsigned int loseRate );
		void					setMangleRate		( unsigned int mangleRate );
	
		void					addNode				( EventNode* n );
		void					addEvent			( Event* e );

		EventNode*				getNode				( unsigned int i );
		EventNode*				getNodeFromAddr		( NodeAddress addr );

		void					removeNode			( EventNode* n );

		SimulatorProtocolStack*	getProtocolStack	( unsigned int i );
		SimulatorProtocolStack*	getProtocolStack	( EventNode* e );
		
		void					getFriends			( EventNode* e, std::vector<EventNode *>& list );
		void					getAcquaintances	( EventNode* e, std::vector<EventNode *>& list );
		
		unsigned int			getNumberOfNodes	( void );
		unsigned int			getNumberOfStacks	( void );
		unsigned int			getNumberOfEvents	( void );
		
		unsigned long			getTicks			( void );
	
		Driver*					getDriver		( void ) { return driver; }
	private:
		std::vector<EventNode*> nodes;
		std::vector<Event*> events;
		std::vector<SimulatorProtocolStack*> stacks;
		Driver* driver;
		std::ofstream* fout;
		
		unsigned int mangled;
		unsigned int lost;
		unsigned int added;
		unsigned int delivered;
		unsigned int collision;
		unsigned int arrived;
		
		unsigned long ticks;
		// the speed at which the events travel trough the simulator space.
		float speed;
		// the maximum range events are allowed to travel
		float range;
		// the errorrate in promiles (1000)
		//unsigned int errorRate; 
		unsigned int mangleRate;
		unsigned int loseRate;
		
		void writeToLog(std::string msg, Event* e);
	};
	
	class Driver
	{
		public:
			virtual			~Driver	();

			void			setSimulator		( Simulator * sim );
			virtual void	run					( int runs );
			virtual void	createWorld			( void ) = 0;
			virtual void	sendDrive			( void ) = 0;
			virtual void	receiveDrive		( void ) = 0;
			virtual void	waitForContinue		( void ) = 0;

			//statistics methods
			virtual void	addEventToSimulator	(Event* e);
			virtual void	recieveEventAtNode	(Event* e);

			// driver-specific features
			virtual float	distanceBetweenPoints	( const Position a, const Position b );
			
			// notifications
			virtual void	notifyCollision			( EventNode* node );
			virtual void	notifyMangle			( Event* event );
			virtual void	notifyArrive			( EventNode* node );
			virtual void	notifySend				( Event* event );
			virtual void	notifyLose				( Event* event );
			virtual void	notifyOutOfRange		( Event* event );
		protected:
			Simulator * sim;
	};
}
